package com.huangfuyi.datamodel.dao;

import cn.hutool.core.bean.BeanUtil;
import com.huangfuyi.common.utils.ObjectUtils;
import com.huangfuyi.datamodel.entity.datasource.EDataSourceConfExample;
import com.huangfuyi.datamodel.entity.datasource.EDataSourceDetailEntity;
import com.huangfuyi.datamodel.entity.datasource.EDataSourceNodeEntity;
import com.huangfuyi.datamodel.exceptions.EDMDataSourceTreeException;
import com.huangfuyi.datasource.entity.EConnectConf;
import com.huangfuyi.datasource.entity.EDataDefineConf;
import com.huangfuyi.datasource.entity.EDataQueryCondition;
import com.huangfuyi.datasource.entity.EDataSelectConf;
import com.huangfuyi.datasource.entity.query.EDataQueryResult;
import com.huangfuyi.datasource.enums.EConditionCompare;
import com.huangfuyi.datasource.enums.EDataCompare;
import com.huangfuyi.datasource.factory.EDataSourceFactory;
import com.huangfuyi.datasource.service.EDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * 数据模型持久层
 */
@Component
public class EDataSourceDao {

    // 数据源树 -- 数据定义
    private EDataDefineConf treeNodeDefine;

    // 数据源 -- 数据定义
    private EDataDefineConf dataSourceDefine;

    // 数据源链接缓存
    private static final Map<String, EDataSource> DATA_SOURCE_CACHE = new HashMap<>();

    @Autowired
    private EDataModelInitDao dataModelInitDao;

    public int createTreeNode(EDataSourceNodeEntity nodeInfo) {
        EDataSource dataSource = dataModelInitDao.getDataSource();
        return dataSource.insert(this.getTreeNodeDefine(), Collections.singletonList(BeanUtil.beanToMap(nodeInfo)));
    }

    public int deleteTreeNode(String id) {
        EDataSource dataSource = dataModelInitDao.getDataSource();
        return dataSource.delete(this.getTreeNodeDefine(), new EDataQueryCondition("id", EDataCompare.EQ, id));
    }

    public int updateTreeNode(EDataSourceNodeEntity nodeInfo) {
        EDataSource dataSource = dataModelInitDao.getDataSource();
        return dataSource.update(this.getTreeNodeDefine(), BeanUtil.beanToMap(nodeInfo), new EDataQueryCondition("id", EDataCompare.EQ, nodeInfo.getId()));
    }

    public EDataQueryResult queryTreeNodeById(String id, boolean needData) {
        EDataSource dataSource = dataModelInitDao.getDataSource();
        return dataSource.select(this.getTreeNodeDefine(), new EDataSelectConf(1, needData ? 1 : 0), new EDataQueryCondition("id", EDataCompare.EQ, id));
    }

    public EDataQueryResult queryTreeNodeByPId(String pid, boolean needData) {
        EDataSource dataSource = dataModelInitDao.getDataSource();
        return dataSource.select(this.getTreeNodeDefine(), new EDataSelectConf(1, needData ? Integer.MAX_VALUE : 0), new EDataQueryCondition("pid", EDataCompare.EQ, pid));
    }

    public EDataQueryResult queryTreeNodeAll() {
        EDataSource dataSource = dataModelInitDao.getDataSource();
        return dataSource.select(this.getTreeNodeDefine(), new EDataSelectConf(1, Integer.MAX_VALUE), new EDataQueryCondition());
    }

    public int addDataSource(EDataSourceDetailEntity dataSourceDetail) {
        EDataSource dataSource = dataModelInitDao.getDataSource();
        return dataSource.insert(this.getDataSourceDefine(), Collections.singletonList(BeanUtil.beanToMap(dataSourceDetail)));
    }

    public int deleteDataSource(String id) {
        EDataSource dataSource = dataModelInitDao.getDataSource();
        return dataSource.delete(this.getDataSourceDefine(), new EDataQueryCondition("id", EDataCompare.EQ, id));
    }

    public int updateDataSource(EDataSourceDetailEntity dataSourceDetail) {
        EDataSource dataSource = dataModelInitDao.getDataSource();
        return dataSource.update(this.getDataSourceDefine(), BeanUtil.beanToMap(dataSourceDetail), new EDataQueryCondition("id", EDataCompare.EQ, dataSourceDetail.getId()));
    }

    public EDataQueryResult queryDataSourceById(String id) {
        EDataSource dataSource = dataModelInitDao.getDataSource();
        return dataSource.select(dataSourceDefine, new EDataSelectConf(), new EDataQueryCondition("id", EDataCompare.EQ, id));
    }

    public EDataQueryResult queryDataSourceByNodeId(String nodeId, int pageNum, int pageSize) {
        EDataSource dataSource = dataModelInitDao.getDataSource();
        EDataSelectConf selectConf = new EDataSelectConf();
        selectConf.setPageNum(pageNum);
        selectConf.setPageSize(pageSize);
        selectConf.setColumnList(new ArrayList<>());
        selectConf.getColumnList().add("id");
        selectConf.getColumnList().add("name");
        selectConf.getColumnList().add("label");
        selectConf.getColumnList().add("nodeId");
        selectConf.getColumnList().add("type");
        return dataSource.select(dataSourceDefine, selectConf, new EDataQueryCondition("nodeId", EDataCompare.EQ, nodeId));
    }

    public EDataQueryResult queryDataSourceByCondition(EDataSourceDetailEntity condition, int pageNum, int pageSize) {
        EDataSource dataSource = dataModelInitDao.getDataSource();
        EDataSelectConf selectConf = new EDataSelectConf();
        selectConf.setPageNum(pageNum);
        selectConf.setPageSize(pageSize);
        selectConf.setColumnList(new ArrayList<>());
        selectConf.getColumnList().add("id");
        selectConf.getColumnList().add("name");
        selectConf.getColumnList().add("label");
        selectConf.getColumnList().add("nodeId");
        selectConf.getColumnList().add("type");
        EDataQueryCondition queryCondition = new EDataQueryCondition(EConditionCompare.AND, new ArrayList<>());
        if (!ObjectUtils.isEmpty(condition.getId())) {
            queryCondition.getSubEDataCondition().add(new EDataQueryCondition("id", EDataCompare.EQ, condition.getId()));
        }
        if (!ObjectUtils.isEmpty(condition.getName())) {
            queryCondition.getSubEDataCondition().add(new EDataQueryCondition("name", EDataCompare.LIKE, condition.getName()));
        }
        if (!ObjectUtils.isEmpty(condition.getLabel())) {
            queryCondition.getSubEDataCondition().add(new EDataQueryCondition("label", EDataCompare.LIKE, condition.getLabel()));
        }
        if (!ObjectUtils.isEmpty(condition.getType())) {
            queryCondition.getSubEDataCondition().add(new EDataQueryCondition("type", EDataCompare.EQ, condition.getType()));
        }
        if (!ObjectUtils.isEmpty(condition.getNodeId())) {
            queryCondition.getSubEDataCondition().add(new EDataQueryCondition("nodeId", EDataCompare.EQ, condition.getNodeId()));
        }
        return dataSource.select(dataSourceDefine, selectConf, queryCondition);
    }


    public EDataDefineConf getTreeNodeDefine() {
        if (ObjectUtils.isEmpty(this.treeNodeDefine)) {
            this.treeNodeDefine = new EDataDefineConf();
            this.treeNodeDefine.setDatabase("EEcologyDataModel");
            this.treeNodeDefine.setSchema("EEcologyDataModel");
            this.treeNodeDefine.setTable("data_source_tree");
        }
        return this.treeNodeDefine;
    }

    public EDataDefineConf getDataSourceDefine() {
        if (ObjectUtils.isEmpty(this.dataSourceDefine)) {
            this.dataSourceDefine = new EDataDefineConf();
            this.dataSourceDefine.setDatabase("EEcologyDataModel");
            this.dataSourceDefine.setSchema("EEcologyDataModel");
            this.dataSourceDefine.setTable("data_source");
        }
        return this.dataSourceDefine;
    }

    public synchronized EDataSource getDataSource(String dataSourceId) {
        if(ObjectUtils.isEmpty(dataSourceId)){
            return null;
        }

        if (EDataSourceDao.DATA_SOURCE_CACHE.containsKey(dataSourceId)) {
            return this.getDataSource(dataSourceId);
        }

        // 数据模型本身使用的数据源信息
        if(EDataModelInitDao.DATA_MODEL_SOURCE_ID.equals(dataSourceId)){
            return dataModelInitDao.getDataSource();
        }

        EDataQueryResult dataSourceDetail = queryDataSourceById(dataSourceId);
        if (ObjectUtils.isEmpty(dataSourceDetail.getData())) {
            throw new EDMDataSourceTreeException(String.format(Locale.ROOT, "未查询到数据源连接信息，id：%s", dataSourceId));
        }

        EDataSourceDetailEntity dataSourceDetailEntity = BeanUtil.toBean(dataSourceDetail.getData().get(0), EDataSourceDetailEntity.class);
        Class<? extends EConnectConf> connectInfoExample = EDataSourceFactory.getConnectInfoExample(dataSourceDetailEntity.getType());
        ArrayList<EConnectConf> connectInfos = new ArrayList<>();
        for (List<EDataSourceConfExample> connectInfo : dataSourceDetailEntity.getConnectInfos()) {
            HashMap<String, Object> connectInfoMap = new HashMap<>();
            for (EDataSourceConfExample example : connectInfo) {
                connectInfoMap.put(example.getName(), example.getValue());
            }
            connectInfos.add(BeanUtil.toBean(connectInfoMap, connectInfoExample));
        }

        EDataSource dataSource = EDataSourceFactory.getDataSource(dataSourceDetailEntity.getType(), connectInfos);
        EDataSourceDao.DATA_SOURCE_CACHE.put(dataSourceId, dataSource);
        return dataSource;
    }

}